﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;

namespace peonwar
{
    public class GameClient : Network
    {
        private Game game;
        private TcpClient Client;
        private NetworkStream Stream;
        public bool Connected;
        private Thread Connecting;
        public override bool AllowReceive { get; set; }
        public int PORT;
        public IPAddress IP;
        public override int SyncDelay { get; set; }

        public GameClient(IPAddress ip, int port, Game g)
        {
            this.game = g;
            IP = ip;
            PORT = port;

            Client = new TcpClient();
            Connecting = new Thread(new ThreadStart(Connect));
            Connecting.IsBackground = true;
            Connecting.Start();
        }

        public void Initialize()
        {
            Stream = Client.GetStream();

            AllowReceive = true;
            Connected = true;

            CommandProcessor.Start(game, this);
        }

        public void Connect()
        {
            try
            {
                DevConsole.AddOutput("Connecting to : " + IP + ":" + PORT);
                Client.Connect(IP, PORT);
                if (Game.gamestate == Gamestates.Join)
                {
                    DevConsole.AddOutput("Connected successfully");
                    game.ConnectionResult(true);
                }
                else
                {
                    Dispose();
                    game.ConnectionResult(false); // connection arreter par le cancel
                    return;
                }
            }
            catch
            {
                if (Game.gamestate == Gamestates.Join)
                {
                    DevConsole.AddOutput("Unable to connect");
                }
                game.ConnectionResult(false);
                return;
            }
            Initialize();
        }

        public override void Disconnect(bool gotodisconnectmenu, string message, string detail)
        {
            AllowReceive = false;
            Dispose();

            game.EndGame();
            game.Join_IsConnecting = false;
            game.Join_Failed = false;
            game.CurrentSession = Session.None;
            MediaPlayer.Play(GameSound.MenuMusic);

            DevConsole.AddOutput("Disconnected - Reason :");
            DevConsole.AddOutput(message);

            if (gotodisconnectmenu)
            {
                game.Disconnected_Message = message;
                game.Disconnected_Detail = detail;
                Game.gamestate = Gamestates.Disconnected;
            }
            else
            {
                Game.gamestate = Gamestates.ChooseVersus;
            }
        }

        #region IO
        public override void Send(RequestType request, object data)
        {
            try
            {
                byte[] CommandByte = new byte[] { (byte)request };
                Stream.Write(CommandByte, 0, CommandByte.Length);

                byte[] Data = CommandProcessor.ObjectToByteArray(data);
                byte[] Size = BitConverter.GetBytes(Data.Length);
                Stream.Write(Size, 0, Size.Length);
                Stream.Write(Data, 0, Data.Length);

            }
            catch (Exception e)
            {
                Disconnect(true, GameData.GameString[218], e.Message);
            }
        }

        public override Tuple<RequestType, object> Receive()
        {
            byte[] IncomingCommandByte = new byte[1];
            byte IncomingCommand;
            Stream.Read(IncomingCommandByte, 0, IncomingCommandByte.Length);
            IncomingCommand = IncomingCommandByte[0];
            RequestType Request = (RequestType)IncomingCommand;

            byte[] IncomingSizeByte = new byte[4];
            int IncomingSize;
            Stream.Read(IncomingSizeByte, 0, IncomingSizeByte.Length);
            IncomingSize = BitConverter.ToInt32(IncomingSizeByte, 0);

            byte[] IncomingData = new byte[IncomingSize];
            CommandProcessor.ReadWholeArray(Stream, IncomingData);
            object dataObject;
            dataObject = CommandProcessor.ByteArrayToObject(IncomingData);

            return new Tuple<RequestType, object>(Request, dataObject);
        }
        #endregion

        #region Tools
        public void Dispose()
        {
            if (Stream != null)
            {
                Stream.Close();
                Stream.Dispose();
            }
            if (Client != null)
                Client.Close();
            Connected = false;
        }
        #endregion

    }
}